{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "05_06_Resnet_CIFAR10.ipynb",
      "provenance": [],
      "collapsed_sections": [],
      "authorship_tag": "ABX9TyM2QGVhMUwBhavlCzvoMjR3",
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "accelerator": "GPU",
    "widgets": {
      "application/vnd.jupyter.widget-state+json": {
        "c6d4c05386224acbbd2f95e384bfe66b": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "HBoxModel",
          "state": {
            "_view_name": "HBoxView",
            "_dom_classes": [],
            "_model_name": "HBoxModel",
            "_view_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_view_count": null,
            "_view_module_version": "1.5.0",
            "box_style": "",
            "layout": "IPY_MODEL_6c722e56904b4baa97f6e999c70749ee",
            "_model_module": "@jupyter-widgets/controls",
            "children": [
              "IPY_MODEL_de7aefde42844e2aafe0f04bf9b4c5cf",
              "IPY_MODEL_248a1085ad2b41a4bd09a60e05f08539"
            ]
          }
        },
        "6c722e56904b4baa97f6e999c70749ee": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "state": {
            "_view_name": "LayoutView",
            "grid_template_rows": null,
            "right": null,
            "justify_content": null,
            "_view_module": "@jupyter-widgets/base",
            "overflow": null,
            "_model_module_version": "1.2.0",
            "_view_count": null,
            "flex_flow": null,
            "width": null,
            "min_width": null,
            "border": null,
            "align_items": null,
            "bottom": null,
            "_model_module": "@jupyter-widgets/base",
            "top": null,
            "grid_column": null,
            "overflow_y": null,
            "overflow_x": null,
            "grid_auto_flow": null,
            "grid_area": null,
            "grid_template_columns": null,
            "flex": null,
            "_model_name": "LayoutModel",
            "justify_items": null,
            "grid_row": null,
            "max_height": null,
            "align_content": null,
            "visibility": null,
            "align_self": null,
            "height": null,
            "min_height": null,
            "padding": null,
            "grid_auto_rows": null,
            "grid_gap": null,
            "max_width": null,
            "order": null,
            "_view_module_version": "1.2.0",
            "grid_template_areas": null,
            "object_position": null,
            "object_fit": null,
            "grid_auto_columns": null,
            "margin": null,
            "display": null,
            "left": null
          }
        },
        "de7aefde42844e2aafe0f04bf9b4c5cf": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "IntProgressModel",
          "state": {
            "_view_name": "ProgressView",
            "style": "IPY_MODEL_d5aa675ff5574a03b676ac75904c7ce8",
            "_dom_classes": [],
            "description": "",
            "_model_name": "IntProgressModel",
            "bar_style": "success",
            "max": 1,
            "_view_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "value": 1,
            "_view_count": null,
            "_view_module_version": "1.5.0",
            "orientation": "horizontal",
            "min": 0,
            "description_tooltip": null,
            "_model_module": "@jupyter-widgets/controls",
            "layout": "IPY_MODEL_b455966f588c4754a15a82534b0351a3"
          }
        },
        "248a1085ad2b41a4bd09a60e05f08539": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "HTMLModel",
          "state": {
            "_view_name": "HTMLView",
            "style": "IPY_MODEL_40d8058cb10d4c82ba764a8bbaca97fe",
            "_dom_classes": [],
            "description": "",
            "_model_name": "HTMLModel",
            "placeholder": "​",
            "_view_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "value": "170500096it [00:06, 26556581.91it/s]",
            "_view_count": null,
            "_view_module_version": "1.5.0",
            "description_tooltip": null,
            "_model_module": "@jupyter-widgets/controls",
            "layout": "IPY_MODEL_c73179f7b7544091bd1c1cfae048f19c"
          }
        },
        "d5aa675ff5574a03b676ac75904c7ce8": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "ProgressStyleModel",
          "state": {
            "_view_name": "StyleView",
            "_model_name": "ProgressStyleModel",
            "description_width": "",
            "_view_module": "@jupyter-widgets/base",
            "_model_module_version": "1.5.0",
            "_view_count": null,
            "_view_module_version": "1.2.0",
            "bar_color": null,
            "_model_module": "@jupyter-widgets/controls"
          }
        },
        "b455966f588c4754a15a82534b0351a3": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "state": {
            "_view_name": "LayoutView",
            "grid_template_rows": null,
            "right": null,
            "justify_content": null,
            "_view_module": "@jupyter-widgets/base",
            "overflow": null,
            "_model_module_version": "1.2.0",
            "_view_count": null,
            "flex_flow": null,
            "width": null,
            "min_width": null,
            "border": null,
            "align_items": null,
            "bottom": null,
            "_model_module": "@jupyter-widgets/base",
            "top": null,
            "grid_column": null,
            "overflow_y": null,
            "overflow_x": null,
            "grid_auto_flow": null,
            "grid_area": null,
            "grid_template_columns": null,
            "flex": null,
            "_model_name": "LayoutModel",
            "justify_items": null,
            "grid_row": null,
            "max_height": null,
            "align_content": null,
            "visibility": null,
            "align_self": null,
            "height": null,
            "min_height": null,
            "padding": null,
            "grid_auto_rows": null,
            "grid_gap": null,
            "max_width": null,
            "order": null,
            "_view_module_version": "1.2.0",
            "grid_template_areas": null,
            "object_position": null,
            "object_fit": null,
            "grid_auto_columns": null,
            "margin": null,
            "display": null,
            "left": null
          }
        },
        "40d8058cb10d4c82ba764a8bbaca97fe": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "DescriptionStyleModel",
          "state": {
            "_view_name": "StyleView",
            "_model_name": "DescriptionStyleModel",
            "description_width": "",
            "_view_module": "@jupyter-widgets/base",
            "_model_module_version": "1.5.0",
            "_view_count": null,
            "_view_module_version": "1.2.0",
            "_model_module": "@jupyter-widgets/controls"
          }
        },
        "c73179f7b7544091bd1c1cfae048f19c": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "state": {
            "_view_name": "LayoutView",
            "grid_template_rows": null,
            "right": null,
            "justify_content": null,
            "_view_module": "@jupyter-widgets/base",
            "overflow": null,
            "_model_module_version": "1.2.0",
            "_view_count": null,
            "flex_flow": null,
            "width": null,
            "min_width": null,
            "border": null,
            "align_items": null,
            "bottom": null,
            "_model_module": "@jupyter-widgets/base",
            "top": null,
            "grid_column": null,
            "overflow_y": null,
            "overflow_x": null,
            "grid_auto_flow": null,
            "grid_area": null,
            "grid_template_columns": null,
            "flex": null,
            "_model_name": "LayoutModel",
            "justify_items": null,
            "grid_row": null,
            "max_height": null,
            "align_content": null,
            "visibility": null,
            "align_self": null,
            "height": null,
            "min_height": null,
            "padding": null,
            "grid_auto_rows": null,
            "grid_gap": null,
            "max_width": null,
            "order": null,
            "_view_module_version": "1.2.0",
            "grid_template_areas": null,
            "object_position": null,
            "object_fit": null,
            "grid_auto_columns": null,
            "margin": null,
            "display": null,
            "left": null
          }
        }
      }
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/OUCTheoryGroup/colab_demo/blob/master/05_06_Resnet_CIFAR10.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "UyFqhzgvy4BQ",
        "colab_type": "text"
      },
      "source": [
        "## Resnet18 应用于 CIFAR10 分类\n",
        "\n",
        "终于可以说一下Resnet分类网络了，它差不多是当前应用最为广泛的CNN特征提取网络。细节大家可以参考这篇论文：\n",
        "\n",
        "Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun, Deep Residual Learning for Image Recognition, *CVPR* 2016.\n",
        "\n",
        "我们的一般印象当中，CNN越深就越具有更强的表达能力。因此，CNN分类网络自Alexnet的7层发展到了VGG的19层，后来有了Googlenet的22层。可后来我们发现深度CNN网络达到一定深度后再一味地增加层数并不能带来进一步地分类性能提高，反而会招致网络收敛变得更慢，分类准确率也变得更差。\n",
        "\n",
        "## 1. BasicBlock\n",
        "\n",
        "何恺明想到了常规视觉领域常用的residual representation的概念，并进一步将它应用在了CNN模型的构建当中，于是就有了基本的residual learning的 BasicBlock。\n",
        "\n",
        "![Resnet](https://gaopursuit.oss-cn-beijing.aliyuncs.com/202003/20200301003958.jpg)\n",
        "\n",
        "BasicBlock 通过Identity mapping的引入在输入、输出之间建立了一条直接的关联通道，从而使得强大的有参层集中精力学习输入、输出之间的残差。简来说，残差更小，学习起来更容易，也收敛更快。\n",
        "\n",
        "下面我们看一下 BasicBlock 的代码：\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "fF_Jqj_Iy3K6",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "import torch\n",
        "import torch.nn as nn\n",
        "import torch.nn.functional as F\n",
        "import torchvision\n",
        "import torchvision.transforms as transforms\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "import torch.optim as optim\n",
        "\n",
        "\n",
        "\n",
        "class BasicBlock(nn.Module):\n",
        "\n",
        "    def __init__(self, in_planes, planes, stride=1):\n",
        "        super(BasicBlock, self).__init__()\n",
        "        self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)\n",
        "        self.bn1 = nn.BatchNorm2d(planes)\n",
        "        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)\n",
        "        self.bn2 = nn.BatchNorm2d(planes)\n",
        "\n",
        "        self.shortcut = nn.Sequential()\n",
        "        if stride != 1 or in_planes != planes:\n",
        "            self.shortcut = nn.Sequential(\n",
        "                nn.Conv2d(in_planes, planes, kernel_size=1, stride=stride, bias=False),\n",
        "                nn.BatchNorm2d(planes)\n",
        "            )\n",
        "\n",
        "    def forward(self, x):\n",
        "        out = F.relu(self.bn1(self.conv1(x)))\n",
        "        out = self.bn2(self.conv2(out))\n",
        "        out += self.shortcut(x)\n",
        "        out = F.relu(out)\n",
        "        return out"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YwbpB3zL2ysr",
        "colab_type": "text"
      },
      "source": [
        "代码比较简单，非常容易看懂，需要注意的是，stride 步长可能是1，也可能是2。如果步长是2的话，两个 feature map 尺寸不一样，就不能做加法了；同样，如果两个 feature map 的通道数不一样，也无法做加法。所有，有一个判断专门处理这个。\n",
        "\n",
        "## 2. Bottleneck Block\n",
        "\n",
        "为了实际计算的考虑，作者提出了一种bottleneck的结构块来代替上面的 Residual block，它像Inception网络那样通过使用1x1 conv来巧妙地缩减或扩张feature map维度从而使得 3x3 的卷积核数目不受外界即上一层输入的影响，自然它的输出也不会影响到下一层，具体图示如下：\n",
        "\n",
        "![Bottleneck](https://gaopursuit.oss-cn-beijing.aliyuncs.com/202003/20200301005443.jpg)\n",
        "\n",
        "实现的代码如下所示："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "1KMluNiC4C2c",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "class Bottleneck(nn.Module):\n",
        "    expansion = 4\n",
        "    \n",
        "    def __init__(self, in_planes, planes, stride=1):\n",
        "        super(Bottleneck, self).__init__()\n",
        "        self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=1, bias=False)\n",
        "        self.bn1 = nn.BatchNorm2d(planes)\n",
        "        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)\n",
        "        self.bn2 = nn.BatchNorm2d(planes)\n",
        "        self.conv3 = nn.Conv2d(planes, self.expansion*planes, kernel_size=1, bias=False)\n",
        "        self.bn3 = nn.BatchNorm2d(self.expansion*planes)\n",
        "\n",
        "        self.shortcut = nn.Sequential()\n",
        "        if stride != 1 or in_planes != self.expansion*planes:\n",
        "            self.shortcut = nn.Sequential(\n",
        "                nn.Conv2d(in_planes, self.expansion*planes, kernel_size=1, stride=stride, bias=False),\n",
        "                nn.BatchNorm2d(self.expansion*planes)\n",
        "            )\n",
        "\n",
        "    def forward(self, x):\n",
        "        out = F.relu(self.bn1(self.conv1(x)))\n",
        "        out = F.relu(self.bn2(self.conv2(out)))\n",
        "        out = self.bn3(self.conv3(out))\n",
        "        out += self.shortcut(x)\n",
        "        out = F.relu(out)\n",
        "        return out"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nvA9Rb8K5UR8",
        "colab_type": "text"
      },
      "source": [
        "## 3. 创建dataloader\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "LxcBXQoR5emP",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 117,
          "referenced_widgets": [
            "c6d4c05386224acbbd2f95e384bfe66b",
            "6c722e56904b4baa97f6e999c70749ee",
            "de7aefde42844e2aafe0f04bf9b4c5cf",
            "248a1085ad2b41a4bd09a60e05f08539",
            "d5aa675ff5574a03b676ac75904c7ce8",
            "b455966f588c4754a15a82534b0351a3",
            "40d8058cb10d4c82ba764a8bbaca97fe",
            "c73179f7b7544091bd1c1cfae048f19c"
          ]
        },
        "outputId": "0f639e70-853a-4c07-8137-30b91aa04f04"
      },
      "source": [
        "# 使用GPU训练，可以在菜单 \"代码执行工具\" -> \"更改运行时类型\" 里进行设置\n",
        "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n",
        "\n",
        "transform_train = transforms.Compose([\n",
        "    transforms.RandomCrop(32, padding=4),\n",
        "    transforms.RandomHorizontalFlip(),\n",
        "    transforms.ToTensor(),\n",
        "    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))])\n",
        "\n",
        "transform_test = transforms.Compose([\n",
        "    transforms.ToTensor(),\n",
        "    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))])\n",
        "\n",
        "trainset = torchvision.datasets.CIFAR10(root='./data', train=True,  download=True, transform=transform_train)\n",
        "testset  = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_test)\n",
        "\n",
        "trainloader = torch.utils.data.DataLoader(trainset, batch_size=128, shuffle=True, num_workers=2)\n",
        "testloader = torch.utils.data.DataLoader(testset, batch_size=128, shuffle=False, num_workers=2)"
      ],
      "execution_count": 5,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./data/cifar-10-python.tar.gz\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "data": {
            "application/vnd.jupyter.widget-view+json": {
              "model_id": "c6d4c05386224acbbd2f95e384bfe66b",
              "version_minor": 0,
              "version_major": 2
            },
            "text/plain": [
              "HBox(children=(IntProgress(value=1, bar_style='info', max=1), HTML(value='')))"
            ]
          },
          "metadata": {
            "tags": []
          }
        },
        {
          "output_type": "stream",
          "text": [
            "\n",
            "Extracting ./data/cifar-10-python.tar.gz to ./data\n",
            "Files already downloaded and verified\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dF9DgMVQ54O6",
        "colab_type": "text"
      },
      "source": [
        "## 4. 创建 ResNet18 网络"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "weU-UKJz5-cd",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "class ResNet18(nn.Module):\n",
        "    def __init__(self):\n",
        "        super(ResNet18, self).__init__()\n",
        "        self.num_classes = 10\n",
        "        self.in_planes = 64\n",
        "\n",
        "        self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)\n",
        "        self.bn1 = nn.BatchNorm2d(64)\n",
        "        self.layer1 = self._make_layer(BasicBlock, 64,  2, stride=1)\n",
        "        self.layer2 = self._make_layer(BasicBlock, 128, 2, stride=2)\n",
        "        self.layer3 = self._make_layer(BasicBlock, 256, 2, stride=2)\n",
        "        self.layer4 = self._make_layer(BasicBlock, 512, 2, stride=2)\n",
        "        self.linear = nn.Linear(512, self.num_classes)\n",
        "\n",
        "    def _make_layer(self, block, planes, num_blocks, stride):\n",
        "        strides = [stride] + [1]*(num_blocks-1)\n",
        "        layers = []\n",
        "        for stride in strides:\n",
        "            layers.append(block(self.in_planes, planes, stride))\n",
        "            self.in_planes = planes\n",
        "        return nn.Sequential(*layers)\n",
        "\n",
        "    def forward(self, x):\n",
        "        out = F.relu(self.bn1(self.conv1(x)))  \n",
        "        # 64 * 32 * 32\n",
        "        out = self.layer1(out)\n",
        "        # 64 * 32 * 32\n",
        "        out = self.layer2(out)\n",
        "        # 128 * 16 * 16    \n",
        "        out = self.layer3(out)\n",
        "        # 256 * 8 * 8   \n",
        "        out = self.layer4(out)\n",
        "        # 512 * 4 * 4        \n",
        "        out = F.avg_pool2d(out, 4)\n",
        "        # 512 * 1 * 1\n",
        "        out = out.view(out.size(0), -1)\n",
        "        out = self.linear(out)\n",
        "        return out"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Ma5aMWXoAyA4",
        "colab_type": "text"
      },
      "source": [
        "实例化网络："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "et4p9thJA0nm",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# 网络放到GPU上\n",
        "net = ResNet18().to(device)\n",
        "criterion = nn.CrossEntropyLoss()\n",
        "optimizer = optim.Adam(net.parameters(), lr=0.001)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "N2BaN2lPAdpw",
        "colab_type": "text"
      },
      "source": [
        "## 5. 模型训练：\n",
        "\n",
        "训练代码和之前的完全一样。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "AoQgn4aAAoad",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 714
        },
        "outputId": "00d41924-2d5f-41a6-c976-c7b2ab133fc3"
      },
      "source": [
        "for epoch in range(10):  # 重复多轮训练\n",
        "    for i, (inputs, labels) in enumerate(trainloader):\n",
        "        inputs = inputs.to(device)\n",
        "        labels = labels.to(device)\n",
        "        # 优化器梯度归零\n",
        "        optimizer.zero_grad()\n",
        "        # 正向传播 +　反向传播 + 优化 \n",
        "        outputs = net(inputs)\n",
        "        loss = criterion(outputs, labels)\n",
        "        loss.backward()\n",
        "        optimizer.step()\n",
        "        # 输出统计信息\n",
        "        if i % 100 == 0:   \n",
        "            print('Epoch: %d Minibatch: %5d loss: %.3f' %(epoch + 1, i + 1, loss.item()))\n",
        "\n",
        "print('Finished Training')"
      ],
      "execution_count": 29,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Epoch: 1 Minibatch:     1 loss: 2.407\n",
            "Epoch: 1 Minibatch:   101 loss: 1.723\n",
            "Epoch: 1 Minibatch:   201 loss: 1.425\n",
            "Epoch: 1 Minibatch:   301 loss: 1.378\n",
            "Epoch: 2 Minibatch:     1 loss: 0.985\n",
            "Epoch: 2 Minibatch:   101 loss: 1.185\n",
            "Epoch: 2 Minibatch:   201 loss: 1.114\n",
            "Epoch: 2 Minibatch:   301 loss: 0.868\n",
            "Epoch: 3 Minibatch:     1 loss: 0.650\n",
            "Epoch: 3 Minibatch:   101 loss: 0.713\n",
            "Epoch: 3 Minibatch:   201 loss: 0.885\n",
            "Epoch: 3 Minibatch:   301 loss: 0.744\n",
            "Epoch: 4 Minibatch:     1 loss: 0.602\n",
            "Epoch: 4 Minibatch:   101 loss: 0.604\n",
            "Epoch: 4 Minibatch:   201 loss: 0.717\n",
            "Epoch: 4 Minibatch:   301 loss: 0.534\n",
            "Epoch: 5 Minibatch:     1 loss: 0.483\n",
            "Epoch: 5 Minibatch:   101 loss: 0.552\n",
            "Epoch: 5 Minibatch:   201 loss: 0.677\n",
            "Epoch: 5 Minibatch:   301 loss: 0.559\n",
            "Epoch: 6 Minibatch:     1 loss: 0.524\n",
            "Epoch: 6 Minibatch:   101 loss: 0.490\n",
            "Epoch: 6 Minibatch:   201 loss: 0.465\n",
            "Epoch: 6 Minibatch:   301 loss: 0.505\n",
            "Epoch: 7 Minibatch:     1 loss: 0.478\n",
            "Epoch: 7 Minibatch:   101 loss: 0.463\n",
            "Epoch: 7 Minibatch:   201 loss: 0.313\n",
            "Epoch: 7 Minibatch:   301 loss: 0.404\n",
            "Epoch: 8 Minibatch:     1 loss: 0.291\n",
            "Epoch: 8 Minibatch:   101 loss: 0.582\n",
            "Epoch: 8 Minibatch:   201 loss: 0.447\n",
            "Epoch: 8 Minibatch:   301 loss: 0.293\n",
            "Epoch: 9 Minibatch:     1 loss: 0.439\n",
            "Epoch: 9 Minibatch:   101 loss: 0.306\n",
            "Epoch: 9 Minibatch:   201 loss: 0.354\n",
            "Epoch: 9 Minibatch:   301 loss: 0.339\n",
            "Epoch: 10 Minibatch:     1 loss: 0.300\n",
            "Epoch: 10 Minibatch:   101 loss: 0.383\n",
            "Epoch: 10 Minibatch:   201 loss: 0.206\n",
            "Epoch: 10 Minibatch:   301 loss: 0.218\n",
            "Finished Training\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "W67Jf1v6DYgN",
        "colab_type": "text"
      },
      "source": [
        "## 6. 模型测试\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Ts0gtLMTDmgp",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        },
        "outputId": "5cf35814-6ae0-49a7-a709-db0f9c6c6826"
      },
      "source": [
        "correct = 0\n",
        "total = 0\n",
        "\n",
        "for data in testloader:\n",
        "    images, labels = data\n",
        "    images, labels = images.to(device), labels.to(device)\n",
        "    outputs = net(images)\n",
        "    _, predicted = torch.max(outputs.data, 1)\n",
        "    total += labels.size(0)\n",
        "    correct += (predicted == labels).sum().item()\n",
        "\n",
        "print('Accuracy of the network on the 10000 test images: %.2f %%' % (\n",
        "    100 * correct / total))"
      ],
      "execution_count": 32,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Accuracy of the network on the 10000 test images: 85.96 %\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ekmMxpt1D74l",
        "colab_type": "text"
      },
      "source": [
        "一个神奇的时刻，准确率在 InceptionV3 的基础上又提高了 0.56 个百分点 ~\n",
        "\n",
        "其实我是故意的，提升是非常容易的，多跑几个 epoch 就行了。\n",
        "\n",
        "现在还留有余地 ~ 因为有了残差学习，我们非常容易的就能够把网络加深，到50层，100层 ~\n",
        "\n",
        "精力有限，这里我就不再跑 ResNet50 了，但代码提供在下面，感兴趣的同学可以自己跑一跑。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "OH_H-E2bEkLY",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "class ResNet50(nn.Module):\n",
        "    def __init__(self, num_classes=10):\n",
        "        super(ResNet, self).__init__()\n",
        "        self.in_planes = 64\n",
        "\n",
        "        self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)\n",
        "        self.bn1 = nn.BatchNorm2d(64)\n",
        "        self.layer1 = self._make_layer(Bottleneck, 64,  3, stride=1)\n",
        "        self.layer2 = self._make_layer(Bottleneck, 128, 4, stride=2)\n",
        "        self.layer3 = self._make_layer(Bottleneck, 256, 6, stride=2)\n",
        "        self.layer4 = self._make_layer(Bottleneck, 512, 3, stride=2)\n",
        "        self.linear = nn.Linear(512*block.expansion, num_classes)\n",
        "\n",
        "    def _make_layer(self, block, planes, num_blocks, stride):\n",
        "        strides = [stride] + [1]*(num_blocks-1)\n",
        "        layers = []\n",
        "        for stride in strides:\n",
        "            layers.append(block(self.in_planes, planes, stride))\n",
        "            self.in_planes = planes * block.expansion\n",
        "        return nn.Sequential(*layers)\n",
        "\n",
        "    def forward(self, x):\n",
        "        out = F.relu(self.bn1(self.conv1(x)))\n",
        "        out = self.layer1(out)\n",
        "        out = self.layer2(out)\n",
        "        out = self.layer3(out)\n",
        "        out = self.layer4(out)\n",
        "        out = F.avg_pool2d(out, 4)\n",
        "        out = out.view(out.size(0), -1)\n",
        "        out = self.linear(out)\n",
        "        return out"
      ],
      "execution_count": 0,
      "outputs": []
    }
  ]
}
